package worker

import (
	"context"
	"errors"
	"fmt"
	"time"

	"zhonghui/console/models"
	"zhonghui/console/warehouse/cfgx"
	"zhonghui/console/warehouse/dbx"
	"zhonghui/console/warehouse/dbx/cache_main"
	"zhonghui/console/warehouse/logx"
	"zhonghui/console/wxutil"

	"github.com/ddliu/go-httpclient"

	jsoniter "github.com/json-iterator/go"
)

// AppAccessTokenWorker 刷新三方应用调用凭据AccessToken
type AppAccessTokenWorker struct {
	store models.Store
}

func NewAppAccessTokenWorker(store models.Store) *AppAccessTokenWorker {
	return &AppAccessTokenWorker{store: store}
}

func (w *AppAccessTokenWorker) Start() {
	w.do()
	t := time.NewTicker(60 * time.Second)
	for {
		select {
		case <-t.C:
			w.do()
		}
	}
}

func (w *AppAccessTokenWorker) do() {
	defer func() {
		if r := recover(); r != nil {
			logx.Zap().Errorw("定时任务异常", "err", errors.New(fmt.Sprintf("%s", r)))
		}
	}()

	rc := cache_main.Client()
	lock, ch, err := dbx.GetRedisLockWithRefresh(rc, dbx.RedisPrefix("locker-AppAccessTokenWorker"), 5*time.Second, time.Second)
	if err != nil {
		return
	}
	defer func() {
		if ch != nil {
			ch <- 1
		}
		lock.Release(context.Background())
	}()

	logx.Zap().Infow("开始检查app_access_token，获取锁成功")
	cfg := cfgx.Cfg().GetStringMapString("wx")

	// 获取token
	token, err := wxutil.GetSuiteAccessToken(cfg["suite_id"])
	if err != nil {
		logx.Zap().Errorw("token从缓存获取失败", "err", err)
		return
	}

	corps, err := w.store.GetAuthorizedCorpList(context.Background(), time.Now().Add(1200*time.Second))
	if err != nil {
		logx.Zap().Errorw("获取corp列表失败", "err", err)
		return
	}

	for _, c := range corps {
		// 检查锁
		if true != dbx.HasRedisLock(lock) {
			logx.Zap().Warnw("corp请求前没有锁放弃写入")
			return
		}
		logx.Zap().Infow("c 开始刷新token", "crop_id", c.Corpid)
		w.refresh(context.Background(), token, &c)
		logx.Zap().Infow("c 成功刷新token", "crop_id", c.Corpid)
	}
}

func (w *AppAccessTokenWorker) refresh(ctx context.Context, token string, c *models.SysCorpInfo) error {
	body := struct {
		AuthCorpID    string `json:"auth_corpid"`
		PermanentCode string `json:"permanent_code"`
	}{
		AuthCorpID:    c.Corpid,
		PermanentCode: c.PermanentCode,
	}
	j, _ := jsoniter.Marshal(body)

	host := cfgx.Cfg().GetString("wx.host")

	url := fmt.Sprintf("%s/cgi-bin/service/get_corp_token?suite_access_token=%s", host, token)

	res, err := httpclient.PostJson(url, j)
	if err != nil {
		return err
	}

	ret, err := res.ToString()
	if err != nil {
		return err
	}

	// 解析返回数据
	data := struct {
		ErrCode     int64  `json:"errcode"`
		ErrMsg      string `json:"errmsg"`
		AccessToken string `json:"access_token"`
		ExpiresIn   int64  `json:"expires_in"`
	}{}
	if err := jsoniter.Unmarshal([]byte(ret), &data); err != nil {
		return err
	}

	// 业务失败
	if data.ErrCode != 0 {
		logx.Zap().Errorw("获取access_token失败", "err", errors.New(data.ErrMsg))
		return errors.New(data.ErrMsg)
	}

	err = wxutil.SetAppAccessToken(c.Corpid, data.AccessToken)
	if err != nil {
		return err
	}

	// 更新数据库
	_, err = w.store.UpdateCorpAccessTokenById(ctx,
		models.UpdateCorpAccessTokenByIdParams{
			ID:          c.ID,
			AccessToken: data.AccessToken,
			ExpireAt:    time.Now().Add(time.Duration(data.ExpiresIn) * time.Second),
		})

	if err != nil {
		return err
	}
	return nil
}
